package net.lastcoder.ifserver.route;

import net.lastcoder.ifserver.module.BaseModule;
import net.lastcoder.ifserver.module.ModuleNames;
import net.lastcoder.ifserver.nio.UnauthorizedException;
import net.lastcoder.ifserver.node.Node;
import net.lastcoder.ifserver.node.NodeManager;
import net.lastcoder.ifserver.packet.Event;
import net.lastcoder.ifserver.packet.EventHandler;
import net.lastcoder.ifserver.packet.Message;
import net.lastcoder.ifserver.packet.Packet;
import net.lastcoder.ifserver.session.Session;
import net.lastcoder.ifserver.session.SessionManager;
import net.lastcoder.ifserver.session.UlaID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

/**包路由器
 * auth: WenYF
 * date: 2016/10/22
 */
public class PacketRouter extends BaseModule {
    private static final Logger Log = LoggerFactory.getLogger(PacketRouter.class);

    private InterceptorManager interceptorManager;
    private EventHandlerManager eventHandlerManager;
    private SessionManager sessionManager;
    private NodeManager nodeManager;
    public PacketRouter() {
        super(ModuleNames.PacketRouter);
    }

    @Override
    public void init() {
        interceptorManager = InterceptorManager.getInstance();
        eventHandlerManager = EventHandlerManager.getInstance();
        sessionManager = SessionManager.getInstance();
        nodeManager = NodeManager.getInstance();
    }

    @Override
    public void reload() {
    }

    @Override
    public void destroy() {

    }

    /**
     * @param packet
     */
    public void route(Packet packet) {
        route(null, packet);
    }

    public void route(Session session, Packet packet) {
        if (packet.getFromAddress() == null && session == null) {
            return;
        }

        UlaID from = packet.getFromAddress();
        if (from == null) {
            from = session.getAddress();
        }

        boolean incoming = from.getResource() == UlaID.RESOURCE_USER;
        route(session, packet, incoming);
    }

    public void route(Session session, Packet packet, boolean incoming) {
        List<PacketInterceptor> messageInterceptors = interceptorManager
                .getMessageInterceptors();
        try {
            for (PacketInterceptor interceptor : messageInterceptors) {
                interceptor.interceptPacket(packet, session, incoming, false);
            }

            // 处理包
            if (packet instanceof Event) {
                Event event = (Event) packet;
                // 先给服务器处理包
                EventHandler eventHandler = eventHandlerManager
                        .getEventHandler(event.getEventType());

                boolean handle = false;
                if (eventHandler != null) {
                    handle = eventHandler.handle(session, event);
                }

                if (!handle) {
                    try {
                        routeEvent(session, event);
                    } catch (Exception e) {
                        Log.error(e.getMessage(), e);
                        if (session != null && !session.isClosed()) {
                            Event error = event.createError();
                            error.setBody(e.getMessage());
                            session.process(error);
                        }
                    }
                }
            } else if (packet instanceof Message) {
                // 找到session
                // TODO 保存?

                Message message = (Message) packet;
                routeMessage(session, message);
            }

            for (PacketInterceptor interceptor : messageInterceptors) {
                interceptor.interceptPacket(packet, session, incoming, true);
            }

        } catch (UnauthorizedException e) {
            // TODO 没有授权
            Log.warn(e.getMessage(), e);
        } catch (PacketRejectedException e) {
            // TODO 拦截处理
            Log.warn(e.getMessage(), e);
        }

    }

    private void routeMessage(Session from, Message message) {
        char dest;
        if (message.getToAddress() != null) {
            dest = message.getToAddress().getResource();
        } else {
            return;
        }
        switch (dest) {
            case UlaID.RESOURCE_SERVER:
                // 发给服务器的
                // 丢弃
                message.setToAddress(null);
                for (Session admin : sessionManager.getAdminSessions()) {
                    admin.process(message);
                }
                break;
            case UlaID.RESOURCE_USER:
                Session session = sessionManager.getSession(message.getTo());
                if (session != null) {
                    session.process(message);
                } else {
                    // TODO 会话为空怎么处理消息
                }
                break;
            default:
                Node node = nodeManager.getNode(dest, message.getToAddress().getUsername());
                if (node != null) {
                    node.processMessage(from, message);
                } else {
                    // TODO 节点为null
                }
                break;

        }
    }

    /**
     * @param event
     */
    private void routeEvent(Session from, Event event) throws Exception {
        char dest;
        if (event.getToAddress() != null) {
            dest = event.getToAddress().getResource();
        } else {
            return;
        }

        switch (dest) {
            case UlaID.RESOURCE_SERVER:
                break;
            case UlaID.RESOURCE_USER:
                Session session = sessionManager.getSession(event.getTo());
                if (session != null) {
                    session.process(event);
                } else {
                    // TODO 会话为空怎么处理
                    if (event.getType() == Packet.TYPE_GET || event.getType() == Packet.TYPE_SET) {
                        event = event.createError();
                        event.setBody("{\"reason\":\"session not found.\"}");
                        from.process(event);
                    }
                }
                break;
            default:
                Node node = nodeManager.getNode(dest, event.getToAddress().getUsername());
                if (node != null) {
                    node.processEvent(from, event);
                }
                break;
        }
    }
}
